// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rainbowchat.token;

import com.eva.framework.utils.EndsExceptionFactory;
import com.eva.framework.utils.LoggerFactory;
import com.x52im.rainbowchat.BaseConf;
import com.x52im.rainbowchat.RequestWhitelist;
import org.slf4j.Logger;

/**
 * Token管理器（根据配置文件中指明的Token加密模式自动使用相应的Token提供者对象提供token服务）。
 * 本类单例实现，方便调用。
 *
 * @author JackJiang
 * @since 10.0
 */
public class TokenManager {

    private static final Logger logger = LoggerFactory.getLog();

    /** 管理器单例 */
    private static volatile TokenManager instance = null;

    /** token提供者 */
    private TokenProvider tokenProvider = null;
    /** 加密模式：true 表示使用对称加密（适用于中安全场景）、false 表示使用非对称加密（适用于高安全场景）。 */
    private boolean isSymmetricMode = true;

    /** 获得管理器单例 */
    public static TokenManager getInstance() {
        if (instance == null) {
            synchronized (TokenManager.class) {
                if (instance == null) {
                    instance = new TokenManager();
                }
            }
        }
        return instance;
    }

    private TokenManager() {
        // 是否使用对称加密模式
        this.isSymmetricMode = BaseConf.getInstance().isSymmetricTokenMode();
        if(this.isSymmetricMode) {
            tokenProvider = new SymmetricTokenProvider();
            logger.debug("【token校验管理器】当前启用的是对称加密模式，token提供者已实例化完毕。");
        } else {
            tokenProvider = new RSATokenProvider();
            logger.debug("【token校验管理器】当前启用的是RSA非对称加密模式，token提供者已实例化完毕。");
        }
    }

    /**
     * 生成token。
     *
     * @param uid 放到token中的uid
     * @return 成功生成则返回token字串，否则抛出异常
     * @throws Exception 成功生成则返回token字串，否则抛出异常
     */
    public String generate(String uid) throws Exception {
        return this.tokenProvider.generateToken(uid);
    }

    /**
     * 检验token。
     *
     * @param token 需要被校验的token字符串
     * @return 为空则表示检验失败，否则检验成功。返回值为uid
     */
    public String verify(String token) {
        return this.tokenProvider.verifyToken(token);
    }

    /**
     * 校验http接口请求token的合法性。
     *
     * @param httpInterfaceNum http接口编号
     * @param token 用户登陆时由服务端生成并返回给客户端的token
     * @return 成功通过检验则会返回token中的uid
     * @throws Exception 校验不通过则抛出异常
     */
    public static String verifyHttpToken(String httpInterfaceNum, String token) throws Exception {
        String uid = null;
        if(BaseConf.getInstance().getTOKEN_FOR_HTTP_VERIFY_ENABLED()) {
            if(RequestWhitelist.getInstance().contains(httpInterfaceNum)) {
                LoggerFactory.getLog().info("[HTTP]<token校验> 当前已启用http token校验机制【√】，但接口"+httpInterfaceNum+"处于白名单中，无需校验，直接放行【√】。。。");
            } else {
                if ((uid = TokenManager.getInstance().verify(token)) == null) {
                    LoggerFactory.getLog().error("[HTTP]<token校验> 当前已启用http token校验机制【√】，接口"+httpInterfaceNum+"不处于白名单中，且校验失败，本次将被拒绝服务【×】！");
                    throw EndsExceptionFactory.INVALID_TOKEN(token);
                } else {
                    LoggerFactory.getLog().info("[HTTP]<token校验> 当前已启用http token校验机制【√】，接口"+httpInterfaceNum+"已顺利通过合法身份校验，直接放行【√】。。。");
                }
            }
        } else {
            LoggerFactory.getLog().warn("[HTTP]<token校验> 当前未启用http token校验机制【☠】，无需校验。");
        }

        return uid;
    }

    /**
     * 校验用户的IM连接/登陆请求token的合法性。
     *
     * @param token 用户登陆时由服务端生成并返回给客户端的token
     * @return 0表示验证通过，否则不通过
     */
    public static int verifySocketToken(String token) {
        if (BaseConf.getInstance().getTOKEN_FOR_SOCKET_VERIFY_ENABLED()) {
            if (TokenManager.getInstance().verify(token) == null) {
                LoggerFactory.getLog().error("[RBChat]<token校验> 当前已启用socket token校验机制【√】，校验失败，本次socket登录/连接将被拒绝【×】！");
                return 1025;
            } else {
                LoggerFactory.getLog().info("[RBChat]<token校验> 当前已启用socket token校验机制【√】，客户端已顺利通过合法身份校验，socket登录/连接验证完成【√】。");
            }
        } else {
            LoggerFactory.getLog().warn("[RBChat]<token校验> 当前未启用socket token校验机制【☠】，无需校验。");
        }

        return 0;
    }

    /**
     * 校验文件上传请求token的合法性。
     *
     * @param token 用户登陆时由服务端生成并返回给客户端的token
     * @return 0表示验证通过，否则不通过
     */
    public static String verifyFileUploadToken(String token, String tag) throws Exception {
        String uid = null;
        if (BaseConf.getInstance().getTOKEN_FOR_FILE_UPLOAD_VERIFY_ENABLED()) {
            if ((uid = TokenManager.getInstance().verify(token)) == null) {
                LoggerFactory.getLog().error("[HTTP"+(tag != null?tag:"文件")+"上传]<token校验> 当前已启用文件上传token校验机制【√】，校验失败，本次上传将被拒绝【×】！");
                throw EndsExceptionFactory.INVALID_TOKEN(token);
            } else {
                LoggerFactory.getLog().info("[HTTP"+(tag != null?tag:"文件")+"上传]<token校验> 当前已启用文件上传token校验机制【√】，客户端已顺利通过合法身份校验【√】。");
            }
        } else {
            LoggerFactory.getLog().warn("[HTTP"+(tag != null?tag:"文件")+"上传]<token校验> 当前未启用文件上传token校验机制【☠】，无需校验。");
        }

        return uid;
    }
}
